" Vim filetype plugin
" Language: SQL
" Maintainer: kyo

if !executable('mysql')
  if !executable('docker')
    finish
  else
    let g:kyo_docker = 'docker run --rm -e "LANG=C.UTF-8" -i '
  endif
endif

" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
  finish
endif

unlet! b:did_ftplugin

if !exists('g:kyo_docker_image')
  let g:kyo_docker_image = 'mysql:5.7'
endif

if exists('g:kyo_docker')
  let g:kyo_docker .= g:kyo_docker_image.' '
else
  let g:kyo_docker = ""
endif

if !exists('g:kyo_sql_host')
  let g:kyo_sql_host = 'localhost'
endif

if !exists('g:kyo_sql_port')
  let g:kyo_sql_port = '3306'
endif

if !exists('g:kyo_sql_user')
  let g:kyo_sql_user = 'root'
endif

if !exists('g:kyo_sql_pwd')
  let g:kyo_sql_pwd = '123456'
endif

if !exists('g:kyo_sql_db')
  let g:kyo_sql_db = 'mysql'
endif

if !exists('g:kyo_remote_host')
  let g:kyo_remote_host = ''
endif

if !exists('g:kyo_remote_port')
  let g:kyo_remote_port = ''
endif

if !exists('g:kyo_ssh_conn_name')
  let g:kyo_ssh_conn_name = ''
endif

if !exists('g:kyo_sql_run_file')
  let g:kyo_sql_run_file = tempname()
endif

let s:title = '-MySQL-'
let s:title_preview = '-Preview-'

let s:preview_type = 'dbs'

let s:zoom_in = 0
let s:zoom_in_preview = 0
let s:keymap = {}
let s:keymap_preview = {}

let s:help_open = 0
let s:help_open_preview = 0
let s:help_text_short = [
  \ '" Press <F1> for help',
  \ '',
\ ]
let s:help_text_short_preview = [
  \ '" Press <F1> for help',
  \ '',
\ ]
let s:help_text = s:help_text_short
let s:help_text_preview = s:help_text_short_preview

let s:last_record_line = 0
let s:last_record_col = 0

let s:last_record_line_preview = 0
let s:last_record_col_preview = 0

" 当前选中的数据库
let s:current_sql_db = g:kyo_sql_db
" 当前选中的表
let s:current_sql_table = ""

let s:sql_if = "IF () THEN\n
\\n
\ELSEIF () THEN\n
\\n
\ELSE\n
\\n
\END IF"

let s:sql_case = "CASE \n
\WHEN THEN\n
\\n
\ELSE\n
\\n
\END CASE"

let s:sql_loop = "kyo: LOOP\n
\  IF 条件表达式 THEN\n
\    LEAVE kyo;\n
\  END IF;\n
\END LOOP kyo;"

let s:sql_repeat = "kyo: REPEAT\n
\\n
\UNTIL 条件表达式 END REPEAT kyo;"

let s:sql_while = "kyo: WHILE 条件表达式 DO\n
\\n
\END WHILE kyo;"

let s:sql_declare = "-- 定义fetch取数据的临时变量\n
\DECLARE cur_name VARCHAR(255);\n
\-- 定义游标结束标识符\n
\DECLARE done INT DEFAULT 0;\n
\-- 定义游标及对应的查询语句\n
\DECLARE o CURSOR for SELECT name FROM student;\n
\-- 定义异常处理, 发生02000异常将done置1(即Fetch语句引用游标位置为最后一行之后)\n
\DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;\n
\\n
\-- 打开游标\n
\OPEN o;\n
\\n
\-- 循环遍历游标对应的查询数据\n
\read_loop: LOOP\n
\  -- 获取游标当前位置数据, into后面对应游标查询语句的列名, 但不能与列名命名相同, 否则获取数据失败\n
\  FETCH o INTO cur_name;\n
\  -- 因为定义了异常处理, 游标位置到最后会将done置True, 为真则退出循环\n
\  IF done THEN\n
\    LEAVE read_loop;\n
\  END IF;\n
\  -- 对获取游标当前位置数据进行操作(insert update delete select)\n
\  SELECT cur_name;\n
\END LOOP;\n
\\n
\-- 关闭游标\n
\CLOSE o;"

let s:create = "CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name (\n
\  id INT PRIMARY KEY AUTO_INCREMENT,\n
\) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;"

let s:func = "DELIMITER $$\n
\CREATE FUNCTION func_name(a TINYINT) RETURNS VARCHAR(12)\n
\BEGIN\n
\\n
\END$$\n
\DELIMITER ;"

let s:procedure = "DELIMITER $$\n
\CREATE PROCEDURE pro_name(IN a INT(11), OUT b INT(11), INOUT c INT(11))\n
\BEGIN\n
\\n
\END$$\n
\DELIMITER ;"

let s:select = "SELECT\n
\  [ALL | DISTINCT | DISTINCTROW ]\n
\    [HIGH_PRIORITY]\n
\    [STRAIGHT_JOIN]\n
\  select_expr [, select_expr ...]\n
\  [FROM table_references\n
\  [WHERE where_condition]\n
\  [GROUP BY {col_name | expr | position}\n
\    [ASC | DESC], ... [WITH ROLLUP]]\n
\  [HAVING where_condition]\n
\  [ORDER BY {col_name | expr | position}\n
\    [ASC | DESC], ...]\n
\  [LIMIT {[offset,] row_count | row_count OFFSET offset}]"

let s:sql_utf8 = "SET @@CHARACTER_SET_SERVER = utf8;\n
\SET @@COLLATION_SERVER = utf8_general_ci;"

" function! s:appendContent(content) {{{
function! s:appendContent(content)
  try
    call append(line('.'), split(a:content, '\n'))
    silent exec 'normal dd'
  catch /.*/
  endtry
  return ''
endfunction
" }}}

" 生成私有配置
" function exsql#KyoMySQLGenConfig() {{{
function exsql#KyoMySQLGenConfig()
  let s = "-- Kyo MySQL IDE\n
\\n
\/* Kyo MySQL IDE Config\n
\@Host ".g:kyo_sql_host."\n
\@User ".g:kyo_sql_user."\n
\@Password ".g:kyo_sql_pwd."\n
\@Port ".g:kyo_sql_port."\n
\@Database ".g:kyo_sql_db."\n
\@Remotehost".(g:kyo_remote_host==''?"":" ".g:kyo_remote_host)."\n
\@Remoteport".(g:kyo_remote_port==''?"":" ".g:kyo_remote_port)."\n
\@Ssh".(g:kyo_ssh_conn_name==''?"":" ".g:kyo_ssh_conn_name)."\n
\KYO MySQL IDE Config */"
  return s:appendContent(s)
endfunction
" }}}

" 生成计算执行时间代码
" function exsql#KyoMySQLGenTime() {{{
function exsql#KyoMySQLGenTime()
  let s = "-- 记录起始时间\n
\set @kyo_time = current_timestamp(3);\n
\\n
\-- 这里输入你想要运行的代码...\n
\\n
\-- 输出执行时间\n
\select found_rows() '查询行', row_count() '影响行', timestampdiff(microsecond, @kyo_time, current_timestamp(3)) / 1000000 '执行时间(秒)';"
  return s:appendContent(s)
endfunction
" }}}

" 创建空文件自动生成模板
" function exsql#KyoMySQLAutoGen() {{{
function exsql#KyoMySQLAutoGen()
  let lastLineNo = line('$')
  if lastLineNo != 1 || len(getline(lastLineNo)) != 0
    silent call s:parseConfig()
    let s:current_sql_db = g:kyo_sql_db
    let s:current_sql_table = ""
    return
  endif

  call exsql#KyoMySQLGenConfig()
  silent exec "normal Go"
  silent exec "normal o"
  call exsql#KyoMySQLGenTime()

  silent call s:parseConfig()
  let s:current_sql_db = g:kyo_sql_db
  let s:current_sql_table = ""
endfunction
" }}}

" 根据内容给全局配置变量赋值
" function! s:assignConfig(config) {{{
function! s:assignConfig(config)
  try
    let config_map = split(a:config)
  catch /.*/
    return
  endtry

  if len(config_map) > 0
    let name = config_map[0]
    if len(config_map) == 1
      if name == "@Remotehost"
        let g:kyo_remote_host = ''
      elseif name == "@Remoteport"
        let g:kyo_remote_port = ''
      elseif name == "@Ssh"
        let g:kyo_ssh_conn_name = ''
      endif
    else
      let value = config_map[1]
      if name == "@Host"
        let g:kyo_sql_host = value
      elseif name == "@User"
        let g:kyo_sql_user = value
      elseif name == "@Port"
        let g:kyo_sql_port = value
      elseif name == "@Password"
        let g:kyo_sql_pwd = value
      elseif name == "@Database"
        let g:kyo_sql_db = value
      elseif name == "@Remotehost"
        let g:kyo_remote_host = value
      elseif name == "@Remoteport"
        let g:kyo_remote_port = value
      elseif name == "@Ssh"
        let g:kyo_ssh_conn_name = value
      endif
    endif
  endif
endfunction
" }}}

" 解析本文私有配置
" function! s:parseConfig() {{{
function! s:parseConfig()
  let re = '\ *kyo\ *mysql\ *ide\ *config\ *'
  let config = []

  let oline = line(".")
  let ocol = col(".")

  " if type(a:content) == 3
    " let start = match(a:content, '\c\/\*'.re)
    " let end = match(a:content, '\c'.re.'\*\/')
    " if start != -1 && end != -1
      " let config = a:content[start + 1 : end - 1]
      " unlet a:content[start : end]
    " endif
  " else
    " silent call cursor(1,1)
    " let start = search('\/\*'.re, 'n') + 1
    " let end = search(re.'\*\/', 'n') - 1
    " let config = getline(start, end)
  " endif

  silent call cursor(1,1)
  let start = search('\/\*'.re, 'n') + 1
  let end = search(re.'\*\/', 'n') - 1

  if start > 1 && end >= start
    let config = getline(start, end)
  else
    let config = []
  endif

  for x in config
    call s:assignConfig(x)
  endfor

  silent call cursor(oline, ocol)
endfunction
" }}}

" 清除列表中空行和SQL注释行
" function! s:clearComment(content_list) {{{
function! s:clearComment(content_list)
  let newlist = []
  for s in a:content_list
    if strlen(s) > 0 && stridx(s, '-- ') == -1
      call add(newlist, s)
    endif
  endfor
  return join(newlist)
endfunction
" }}}

" 解析配置执行mysql命令并且分割窗口显示
" function exsql#KyoMySQLCmdView(isVisual) range {{{
function exsql#KyoMySQLCmdView(isVisual) range
  call s:parseConfig()
  call exsql#BuildSshTunnel()

  if a:isVisual
    let content_list = [ "set @kyo_time = current_timestamp(3);" ] + exsql#GetVisualSelection(a:firstline, a:lastline)
      \ + [ "select found_rows() '查询行', row_count() '影响行', timestampdiff(microsecond,
      \ @kyo_time, current_timestamp(3)) / 1000000 '执行时间(秒)';" ]
  else
    let content_list = getline(1, line("$"))
  endif

  silent call writefile(content_list, g:kyo_sql_run_file)

  let cmd = g:kyo_docker . "mysql -h" . g:kyo_sql_host . " -P" . g:kyo_sql_port
  let cmd .= " -u" . g:kyo_sql_user . " -p" . g:kyo_sql_pwd . " " . g:kyo_sql_db
  let cmd .= " -t < " . g:kyo_sql_run_file

  let result = system(cmd)

  call exsql#open_window()

  setlocal modifiable
  " clear contents
  silent exec '1,$d _'
  silent call append( 0, exsql#filterUnusedLines(split(result, "\n")) )
  silent exec 'normal! dd'
  silent exec 'normal! zb'
  setlocal nomodifiable

  call ex#window#goto_edit_window()
endfunction
" }}}

" function exsql#BuildSshTunnel() {{{
function exsql#BuildSshTunnel()
  if g:kyo_remote_host != "" && g:kyo_remote_port != "" && g:kyo_ssh_conn_name != ""
    let start_cmd = "ssh -fNL " . g:kyo_sql_port . ":" . g:kyo_remote_host . ":" . g:kyo_remote_port . " " . g:kyo_ssh_conn_name
    let cmd = "ps -ef | grep '" . substitute(start_cmd, '\.', '\\.', 'g') . "' | grep -v 'grep'"
    silent call system(cmd)
    if v:shell_error
      silent call system(start_cmd)
      sleep
    endif
  endif
  return 1
endfunction
" }}}

" function exsql#filterUnusedLines() {{{
function exsql#filterUnusedLines(data)
  let data = a:data
  if type(data) != 3
    return data
  endif
  if data[0] =~ "^mysql:.*Warning"
    let data = data[1:]
  endif
  return data
endfunction
" }}}

" 执行mysql命令并且返回结果
" function! s:mysqlExec(sql, fmt) {{{
function! s:mysqlExec(sql, fmt)
  if len(a:sql) == 0
    return ''
  endif

  call s:parseConfig()
  let cmd = g:kyo_docker . "mysql -h" . g:kyo_sql_host . " -P" . g:kyo_sql_port
  let cmd .= " -u" . g:kyo_sql_user . " -p" . g:kyo_sql_pwd . " " . g:kyo_sql_db
  call exsql#BuildSshTunnel()

  if a:fmt
    let cmd .= " -t "
  endif
  let cmd .= " <<< '" . a:sql . "'"

  try
    let out = split(system(cmd), '\n')
    if out[0] =~ "^mysql:.*Warning"
      let out = out[1:]
    endif

    if out[0] =~ "^Tables_in" || out[0] =~ "^Database"
      let out = out[1:]
    elseif out[0] =~ "^Field\\s*Type"
      let out = out[1:]
      let index = 0

      while index < len(out)
        let out[index] = substitute(out[index], '\(^\w\+\).*', '\1', '')
        let index = index + 1
      endwhile
    elseif out[0] =~ "^Table\\s*Create\\s*Table"
      let out = out[1:]
      if len(out) == 1
        let out[0] = substitute(out[0], '\\n', '\n', 'g')
        let out = split(out[0], '\n')
      endif

      let out[0] = substitute(out[0], '^\w\+\s*\(CREATE\s*TABLE\)', '\1', '')
      let out[-1] = out[-1] . ';'
    endif

    return out
  catch /.*/
    return []
  endtry
endfunction
" }}}

" function exsql#GenerateTableCrudSql(name, table) range {{{
function exsql#GenerateTableCrudSql(name, table, isVisual) range
  if a:isVisual
    let table = join(exsql#GetPreviewVisualSelection(a:firstline, a:lastline))
  else
    let table = a:table
  endif

  let sql = ''
  if a:name == "select-count"
    let sql .= 'SELECT COUNT(*) FROM `' . table . '`;'
  else
    if a:name == "select-all"
      let sql .= 'SELECT '
    elseif a:name == "insert"
      let sql .= 'INSERT INTO `' . table . '` ('
    elseif a:name == "update"
      let sql .= 'UPDATE `' . table . '` SET '
    else
      return 1
    endif

    silent call s:parseConfig()
    let out = s:mysqlExec('show columns from `' . g:kyo_sql_db . '`.`' . table . '`', 0)

    let index = 0
    while index < len(out)
      let sql .= '`' . out[index] . '`'
      if a:name == "update"
        let sql .= " = ''"
      endif

      if index < len(out) - 1
        let sql .= ', '
      endif
      let index = index + 1
    endwhile

    if a:name == "select-all"
      let sql .= ' FROM `' . table . '` LIMIT 10;'
    elseif a:name == "insert"
      let sql .= ') VALUES ('
      let inner_index = 0

      while inner_index < len(out)
        let sql .= "''"
        if inner_index < len(out) - 1
          let sql .= ', '
        endif
        let inner_index = inner_index + 1
      endwhile

      let sql .= ');'
    else
      let sql .= ' WHERE `id` = 0;'
    endif
  endif

  call s:appendContent(sql)
endfunction
" }}}

" 查看表结构
" function exsql#showColumns(key, jumpkey, goto, isVisual) range {{{
function exsql#showColumns(key, jumpkey, goto, isVisual) range
  let pos_line = 1
  if a:isVisual
    let local_jumpkey = join(exsql#GetPreviewVisualSelection(a:firstline, a:lastline))
  else
    if a:key == "enter" || a:key == "shift+enter"
      let local_jumpkey = getline(".")
    else
      let local_jumpkey = a:jumpkey
    endif
  endif

  if a:key == "enter"
    if local_jumpkey =~ "^--" || local_jumpkey =~ "^\\s*$"
      return 1
    endif
    if s:preview_type == "dbs"
      let s:preview_type = "tables"
      let s:current_sql_db = substitute(substitute(local_jumpkey, '^\s*', '', 'g'), '\s*$', '', 'g')
      let out = s:mysqlExec('show tables in `' . s:current_sql_db . '`', 0)
      let out = insert(insert(out, ''), '-- Tables')
    elseif s:preview_type == "tables"
      let s:preview_type = "columns"
      let s:current_sql_table = substitute(substitute(local_jumpkey, '^\s*', '', 'g'), '\s*$', '', 'g')
      let out = s:mysqlExec('show create table `' . s:current_sql_db . '`.`' . s:current_sql_table . '`', 0)
      let out = insert(insert(out, ''), '-- Table')
    else
      return 1
    endif
  elseif a:key == "shift+enter"
    if s:preview_type == "columns"
      let s:preview_type = "tables"
      let out = s:mysqlExec('show tables in `' . s:current_sql_db . '`', 0)
      let out = insert(insert(out, ''), '-- Tables')
      if s:current_sql_table != ""
        let index = match(out, '^' . s:current_sql_table . '$')
        if index != -1
          let pos_line = index + 1
        endif
        let s:current_sql_table = ""
      endif
    elseif s:preview_type == "tables"
      let s:preview_type = "dbs"
      let out = insert(insert(s:mysqlExec('show databases', 0), ''), '-- Databases')
      if s:current_sql_db != ""
        let index = match(out, '^' . s:current_sql_db . '$')
        if index != -1
          let pos_line = index + 1
        endif
      endif
    else
      return 1
    endif
  else
    if a:key == "dbs"
      let s:preview_type = "dbs"
      let out = insert(insert(s:mysqlExec('show databases', 0), ''), '-- Databases')
    elseif a:key == "tables"
      silent call s:parseConfig()
      let s:current_sql_db = g:kyo_sql_db
      let s:preview_type = "tables"
      let out = s:mysqlExec('show tables in `' . s:current_sql_db . '`', 0)
      let out = insert(insert(out, ''), '-- Tables')
    elseif a:key == "columns"
      if local_jumpkey == ""
        return 1
      endif
      silent call s:parseConfig()
      let s:current_sql_db = g:kyo_sql_db
      let s:preview_type = "columns"
      let local_jumpkey = substitute(substitute(local_jumpkey, '^\s*', '', 'g'), '\s*$', '', 'g')
      let s:current_sql_table = local_jumpkey
      let out = s:mysqlExec('show create table `' . s:current_sql_db . '`.`' . local_jumpkey . '`', 0)
      let out = insert(insert(out, ''), '-- Table')
    else
      return 1
    endif
  endif

  call exsql#open_preview_window()

  setlocal modifiable
  " clear contents
  silent exec '1,$d _'
  silent call append(0, out)
  silent exec 'normal! dd'
  silent exec 'normal! gg'
  silent call cursor(pos_line, 1)
  setlocal nomodifiable

  if a:goto
    call ex#window#goto_edit_window()
  endif
endfunction
" }}}

" 查询SQL并导出Excel文件
" function exsql#mysqlExport() range {{{
function exsql#mysqlExport() range
  call s:parseConfig()
  call exsql#BuildSshTunnel()

  " 查询sql
  let content_list = exsql#GetPreviewVisualSelection(a:firstline, a:lastline)
  silent call map(content_list, {key, val -> trim(substitute(val, '--\s*.*$', '', 'g'))})
  silent call filter(content_list, {idx, val -> match(val, '^\s*$') == -1})
  let sql = join(content_list)

  " 导出工具jar文件
  let export_jar = expand(g:ex_tools_path) . 'export/' . 'export-utils-1.0.jar'
  if !filereadable(export_jar)
    return
  endif

  " 检查jvm
  if !executable('java')
    return
  endif

  " 目录不存在就创建
  let export_dir_name = expand(g:exvim_project_root) . '/data/export'
  if !isdirectory(export_dir_name)
    silent call mkdir(export_dir_name, 'p')
  endif

  " 导出文件存在先删除再创建
  let export_file_name = export_dir_name . '/' . 'DATA_EXPORT_' . trim(system("date +'%y-%m-%d_%H:%M'")) . '.xlsx'
  if filereadable(export_file_name)
      silent call delete(export_file_name)
  endif

  " 组装导出命令
  let export_log_file = '/tmp/mysql-export.log'
  let kyo_sql_url = 'jdbc:mysql://' . g:kyo_sql_host . ':' . g:kyo_sql_port
  let export_cmd = 'java -Dapp.mysql.sql=' . shellescape(sql)
              \ . ' -Dapp.mysql.file=' . shellescape(export_file_name)
              \ . ' -Dapp.mysql.url=' . shellescape(kyo_sql_url)
              \ . ' -Dapp.mysql.username=' . shellescape(g:kyo_sql_user)
              \ . ' -Dapp.mysql.password=' . shellescape(g:kyo_sql_pwd)
              \ . ' -Dapp.mysql.database=' . shellescape(g:kyo_sql_db)
              \ . ' -jar ' . export_jar . ' &> ' . export_log_file

  echo 'SQL Exporting...'
  silent call system(export_cmd)
  redraw!

  if v:shell_error
      echo 'SQL Export Failed: Please View ' . export_log_file
  else
      echo 'SQL Exported.'
  endif
endfunction
" }}}

" function exsql#KyoCompleteDatabase(findstart, base) {{{
function exsql#KyoCompleteDatabase(findstart, base)
  return exsql#KyoComplete(a:findstart, a:base, s:mysqlExec('show databases', 0))
endfunction
" }}}

" function exsql#KyoCompleteTable(findstart, base) {{{
function exsql#KyoCompleteTable(findstart, base)
  return exsql#KyoComplete(a:findstart, a:base, s:mysqlExec('show tables', 0))
endfunction
" }}}

" function exsql#KyoCompleteFunction(findstart, base) {{{
function exsql#KyoCompleteFunction(findstart, base)
  let sql_doc_rtp = filter(split(&rtp, ','), 'v:val =~ "ex-sql"')
  if len(sql_doc_rtp) == 1
    let funcs_complete_txt = sql_doc_rtp[0] . 'doc/funcs-complete.txt'
    if filereadable(funcs_complete_txt)
      return exsql#KyoComplete(a:findstart, a:base, readfile(funcs_complete_txt))
    endif
  endif
endfunction
" }}}

" function exsql#TriggerComplete(name) {{{
function exsql#TriggerComplete(name)
  if a:name == 'table'
    setlocal completefunc=exsql#KyoCompleteTable
  elseif a:name == 'db'
    setlocal completefunc=exsql#KyoCompleteDatabase
  elseif a:name == 'func'
    setlocal completefunc=exsql#KyoCompleteFunction
  endif
  return "\<C-X>\<C-U>"
endfunction
" }}}

" 运行mysql命令并且弹出选择框选择
" function exsql#ListData(cmd) {{{
function exsql#ListData(cmd)
  let out = s:mysqlExec(a:cmd, 0)
  let list = split(out, '\n')
  call complete(col('.'), list[1:])
  return ''
endfunction
" }}}

" function exsql#bind_mappings {{{
function exsql#bind_mappings()
  call ex#keymap#bind( s:keymap )
endfunction
" }}}

" function exsql#bind_preview_mappings {{{
function exsql#bind_preview_mappings()
  call ex#keymap#bind( s:keymap_preview )
endfunction
" }}}

" function exsql#register_hotkey {{{
function exsql#register_hotkey( priority, local, key, action, desc )
  call ex#keymap#register( s:keymap, a:priority, a:local, a:key, a:action, a:desc )
endfunction
" }}}

" function exsql#register_preview_hotkey {{{
function exsql#register_preview_hotkey( priority, local, key, action, desc )
  call ex#keymap#register( s:keymap_preview, a:priority, a:local, a:key, a:action, a:desc )
endfunction
" }}}

" function s:update_help_text {{{
function s:update_help_text()
  if s:help_open
    let s:help_text = ex#keymap#helptext(s:keymap)
  else
    let s:help_text = s:help_text_short
  endif
endfunction
" }}}

" function s:update_help_preview_text {{{
function s:update_help_preview_text()
  if s:help_open_preview
    let s:help_text_preview = ex#keymap#helptext(s:keymap_preview)
  else
    let s:help_text_preview = s:help_text_short_preview
  endif
endfunction
" }}}

" function exsql#toggle_help() {{{
function exsql#toggle_help()
  if !g:ex_sql_enable_help
    return
  endif

  let s:help_open = !s:help_open
  silent exec '1,' . len(s:help_text) . 'd _'
  call s:update_help_text()
  silent call append ( 0, s:help_text )
  silent exec 'normal! dd'
  silent keepjumps normal! gg
  call ex#hl#clear_confirm()
endfunction
" }}}

" function exsql#toggle_help_preview() {{{
function exsql#toggle_help_preview()
  if !g:ex_sql_preview_enable_help
    return
  endif

  let s:help_open_preview = !s:help_open_preview
  silent exec '1,' . len(s:help_text_preview) . 'd _'
  call s:update_help_preview_text()
  silent call append ( 0, s:help_text_preview )
  silent exec 'normal! dd'
  silent keepjumps normal! gg
  call ex#hl#clear_confirm()
endfunction
" }}}

" function exsql#init_buffer {{{
function exsql#init_buffer()
  set filetype=exsql
  augroup exsql
    au! BufWinLeave <buffer> call <SID>on_close()
  augroup END
endfunction
" }}}

" function exsql#init_preview_buffer {{{
function exsql#init_preview_buffer()
  set filetype=exsqlpreview
  augroup exsqlpreview
    au! BufWinLeave <buffer> call <SID>on_preview_close()
  augroup END
endfunction
" }}}

" function s:on_close() {{{
function s:on_close()
  let s:zoom_in = 0
  let s:help_open = 0
  let s:last_record_line = line('.')
  let s:last_record_col = col('.')

  " go back to edit buffer
  call ex#window#goto_edit_window()
endfunction
" }}}

" function s:on_preview_close() {{{
function s:on_preview_close()
  let s:zoom_in_preview = 0
  let s:help_open_preview = 0
  let s:last_record_line_preview = line('.')
  let s:last_record_col_preview = col('.')

  " go back to edit buffer
  call ex#window#goto_edit_window()
endfunction
" }}}

" function exsql#open_window() {{{
function exsql#open_window()
  let winnr = winnr()
  if ex#window#check_if_autoclose(winnr)
    call ex#window#close(winnr)
  endif
  call ex#window#goto_edit_window()

  " 关闭冲突的插件窗口
  let winnr = bufwinnr(s:title)
  let mogwinnr = bufwinnr("-MongoDB-")
  let qfwinnr = bufwinnr("-QFix-")
  let jrwinnr = bufwinnr("-JavaSR-")
  let undotreewinnr = bufwinnr("undotree_0")
  let diffpanelwinnr = bufwinnr("diffpanel_0")

  if mogwinnr != -1
    call ex#window#close(mogwinnr)
  endif
  if qfwinnr != -1
    call ex#window#close(qfwinnr)
  endif
  if jrwinnr != -1
    call ex#window#close(jrwinnr)
  endif
  if undotreewinnr != -1 && diffpanelwinnr != -1 && exists ( ':UndotreeHide' )
    silent exec "UndotreeHide"
  endif

  if winnr == -1
    call ex#window#open(
      \ s:title,
      \ g:ex_sql_winsize,
      \ g:ex_sql_winpos,
      \ 1,
      \ 1,
      \ function('exsql#init_buffer')
      \ )
  else
    exe winnr . 'wincmd w'
  endif

  if !s:last_record_line && !s:last_record_col
    silent execute 'normal! G'
  else
    silent call cursor(s:last_record_line, s:last_record_col)
  endif
endfunction
" }}}

" function exsql#open_preview_window() {{{
function exsql#open_preview_window()
  let winnr = winnr()
  if ex#window#check_if_autoclose(winnr)
    call ex#window#close(winnr)
  endif
  call ex#window#goto_edit_window()

  " 关闭冲突的插件窗口
  let winnr = bufwinnr(s:title_preview)

  if winnr == -1
    if s:preview_type == "dbs" || s:preview_type == "tables"
      let ex_sql_preview_winsize = g:ex_sql_preview_dbs_winsize
    else
      let ex_sql_preview_winsize = g:ex_sql_preview_columns_winsize
    endif
    call ex#window#open(
      \ s:title_preview,
      \ ex_sql_preview_winsize,
      \ g:ex_sql_preview_winpos,
      \ 1,
      \ 1,
      \ function('exsql#init_preview_buffer')
      \ )
  else
    exe winnr . 'wincmd w'
    if s:preview_type == "dbs" || s:preview_type == "tables"
      call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_dbs_winsize )
    else
      call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_columns_winsize )
    endif
  endif

  if !s:last_record_line_preview && !s:last_record_col_preview
    silent execute 'normal! G'
  else
    silent call cursor(s:last_record_line_preview, s:last_record_col_preview)
  endif
endfunction
" }}}

" function exsql#toggle_window {{{
function exsql#toggle_window()
  let result = exsql#close_window()
  if result == 0
    call exsql#open_window()
  endif
endfunction
" }}}

" function exsql#toggle_preview_window {{{
function exsql#toggle_preview_window()
  let result = exsql#close_preview_window()
  if result == 0
    call exsql#open_preview_window()
  endif
endfunction
" }}}

" function exsql#close_window {{{
function exsql#close_window()
  let winnr = bufwinnr(s:title)
  if winnr != -1
    call ex#window#close(winnr)
    return 1
  endif
  return 0
endfunction
" }}}

" function exsql#close_preview_window {{{
function exsql#close_preview_window()
  let winnr = bufwinnr(s:title_preview)
  if winnr != -1
    call ex#window#close(winnr)
    return 1
  endif
  return 0
endfunction
" }}}

" function exsql#toggle_zoom {{{
function exsql#toggle_zoom()
  let winnr = bufwinnr(s:title)
  if winnr != -1
    if s:zoom_in == 0
      let s:zoom_in = 1
      call ex#window#resize( winnr, g:ex_sql_winpos, g:ex_sql_winsize_zoom )
    else
      let s:zoom_in = 0
      call ex#window#resize( winnr, g:ex_sql_winpos, g:ex_sql_winsize )
    endif
  endif
endfunction
" }}}

" function exsql#toggle_preview_zoom {{{
function exsql#toggle_preview_zoom()
  let winnr = bufwinnr(s:title_preview)
  if winnr != -1
    if s:zoom_in_preview == 0
      let s:zoom_in_preview = 1
      if s:preview_type == "dbs" || s:preview_type == "tables"
        call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_dbs_winsize_zoom )
      else
        call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_columns_winsize_zoom )
      endif
    else
      let s:zoom_in_preview = 0
      if s:preview_type == "dbs" || s:preview_type == "tables"
        call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_dbs_winsize )
      else
        call ex#window#resize( winnr, g:ex_sql_preview_winpos, g:ex_sql_preview_columns_winsize )
      endif
    endif
  endif
endfunction
" }}}

" function exsql#KyoSQLAbbr(content) {{{
function exsql#KyoSQLAbbr(content)
  try
    let v = eval('s:'.a:content)
  catch /.*/
    return ''
  endtry
  return s:appendContent(v)
endfunction
" }}}

" 返回可视模式下已选中内容(必须是可视模式下调用)
" function exsql#GetVisualSelection() {{{
function exsql#GetVisualSelection(startline, endline)
  let col1 = getpos("'<")[2]
  let col2 = getpos("'>")[2]
  let lines = getline(a:startline, a:endline)
  let lines[-1] = lines[-1][:col2 - (&selection == 'inclusive' ? 1 : 2)]
  let lines[0] = lines[0][col1 - 1:]
  let lines[-1] = substitute(lines[-1], ';$', '', '') . ';'
  return lines
endfunction
" }}}

" 返回可视模式下已选中内容(必须是可视模式下调用)
" function exsql#GetPreviewVisualSelection() {{{
function exsql#GetPreviewVisualSelection(startline, endline)
  let col1 = getpos("'<")[2]
  let col2 = getpos("'>")[2]
  let lines = getline(a:startline, a:endline)
  let lines[-1] = lines[-1][:col2 - (&selection == 'inclusive' ? 1 : 2)]
  let lines[0] = lines[0][col1 - 1:]
  return lines
endfunction
" }}}

" 自定义补全通用函数 ctrl+x,ctrl+u触发
" function! KyoComplete(findstart, base, list) {{{
function exsql#KyoComplete(findstart, base, list)
  if a:findstart
    " 定位单词的开始处
    let line = getline('.')
    let start = col('.') - 1
    while start > 0 && line[start - 1] =~ '\w'
      let start -= 1
    endwhile
    return start
  else
    let res = []
    for m in a:list
      if m =~ '^' . a:base
        call add(res, m)
      endif
    endfor
    return res
  endif
endfunction
" }}}

" vim:ts=4:sw=4:sts=4 et fdm=marker:
